package test.threads.queue.unstressed.impl;

import java.util.concurrent.locks.ReentrantLock;

import test.threads.queue.unstressed.IBoundedQueueFactory;
import test.threads.queue.unstressed.IQueue;

/**
 * ABQ with busy-wait instead of condition-based notification
 *
 * @author cheremin
 * @since 24.03.12,  1:57
 */
public class ABQConditionFreeQueue<T> implements IQueue<T> {

    private static final boolean USE_YIELD_BACKOFF = false;


    private final T[] items;
    /** items index for next take, poll or remove */
    private int takeIndex = 0;
    /** items index for next put, offer, or add. */
    private int putIndex = 0;
    /** Number of items in the queue */
    private int count = 0;

    private final ReentrantLock lock = new ReentrantLock();

    @SuppressWarnings( "unchecked" )
    public ABQConditionFreeQueue( final int size ) {
        this.items = ( T[] ) new Object[size];
    }

    private int inc( int i ) {
        return ( ++i == items.length ) ? 0 : i;
    }

    @Override
    public void enqueue( final T item ) throws InterruptedException {
        for ( int i = 0; ; i++ ) {
            if ( count < items.length
                    && tryAcquireLock() ) {
                try {
                    if ( count < items.length ) {
                        items[putIndex] = item;
                        putIndex = inc( putIndex );
                        ++count;
                        return;
                    }
                } finally {
                    releaseLock();
                }
            }
            backoff( i );
        }
    }

    @Override
    public T dequeue() throws InterruptedException {
        for ( int i = 0; ; i++ ) {
            if ( count > 0 && tryAcquireLock() ) {
                try {
                    if ( count > 0 ) {
                        final T item = items[takeIndex];
                        items[takeIndex] = null;
                        takeIndex = inc( takeIndex );
                        --count;
                        return item;
                    }
                } finally {
                    releaseLock();
                }
            }
            backoff( i );
        }
    }

    private void backoff( final int turn ) throws InterruptedException {
        if ( USE_YIELD_BACKOFF ) {
            Thread.yield();
        }

        /*if ( turn % 10 == 9 ) {
            Thread.yield();
        } else {
            for ( int i = 0; i < turn; i++ ) {
                if ( Thread.currentThread().isInterrupted() ) {
                    return;
                }
            }
        }*/

        /*final int mod = turn & 0xf;
        if ( mod != 0 ) {
            //nothing
        } else {
            if ( ( turn & 0xff ) == 0 ) {
                Thread.sleep( 100 );
            } else {
                Thread.yield();
            }
        }*/
    }

    private void releaseLock() {
        lock.unlock();
    }

    private boolean tryAcquireLock() throws InterruptedException {
        return lock.tryLock();
    }

    @Override
    public int size() {
        //fixme: not correct without membar
        return count;
    }

    @SuppressWarnings( "unchecked" )
    public static <T> IBoundedQueueFactory<T, ABQConditionFreeQueue<T>> factory() {
        return FACTORY;
    }

    public static final IBoundedQueueFactory FACTORY = new IBoundedQueueFactory<Object, ABQConditionFreeQueue<Object>>() {
        @Override
        public ABQConditionFreeQueue<Object> create( final int size ) {
            return new ABQConditionFreeQueue<Object>( size );
        }

        @Override
        public String toString() {
            return "ABQConditionFreeFactory[" + ( USE_YIELD_BACKOFF ? "yield-backoff" : "no-backoff" ) + "]";
        }
    };
}
